Client, Manager, API, docker_wrapper: support network access from apps - #7084
Conversation
This goal of this PR is to allow apps that do network communication to work properly in the presence of network outage and suspension. - If a job that needs network (such as a docker job) runs while the network is disconnected, it figures this out, tells the client, and the user is shown a notice suggesting that they reconnect. - If such a job needs network and network access is suspended by the user (via time-of-day prefs or the 'suspend network' command) it sees this, tells the client that it needs to communicate, and the user is shown a notice suggesting that they unsuspend. This requires some logic in apps; this PR adds this to docker_wrapper. This is described here: https://github.com/BOINC/boinc/wiki/Apps-that-do-network-communication Note: in ~2004 there was an unsuccessful effort to run Folding@home as a BOINC app, and we added logic where the client is responsible for knowing whether a connection exists, and it notifies the app. This PR removes this logic; in the new design it's up to the app to detect the absence of a connection. Remove an API where the app tells the client how much data it transferred. This info isn't used, and in the case of Docker the app doesn't know how much data was transferred.
…ed. WTF? So let's try running ping as a separate command instead
- the ping you get from cmd (or program) is different from the one in powershell - the -c option (to do only 1 ping) gives a permissions error - the output is completely different from Unix ping
There was a problem hiding this comment.
3 issues found across 25 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
Pull request overview
This PR updates the BOINC client/app interface and UI to support applications that require network access during execution, particularly handling (1) physical disconnection and (2) user-suspended network access. It also removes legacy app→client network-usage accounting and the older client→app “network available” signaling design.
Changes:
- Add an app-driven “waiting for network” signal (
boinc_waiting_for_network()/<want_network>) and expose it via GUI RPC/Manager UI (“Waiting for network”). - Update client network-status logic and notices to reflect tasks needing network, and remove legacy network-availability signaling and byte accounting.
- Extend
docker_wrapperto detect network unavailability/suspension during image build and to report “waiting for network”.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| samples/example_app/uc2.cpp | Removes the sample’s deprecated boinc_network_usage() option. |
| samples/docker_wrapper/docker_wrapper.cpp | Adds “waiting for network” handling during image build; refactors output parsing helpers. |
| samples/docker_wrapper/checkpoint_notes | Moves checkpoint/restart notes out of the C++ source. |
| lib/util.h | Updates run_command() signature to accept const char*. |
| lib/util.cpp | Updates run_command() signature definition accordingly. |
| lib/network.h | Adds network_connected() declaration and centralizes addr_len() helper. |
| lib/network.cpp | Implements network_connected() using ping. |
| lib/gui_rpc_client.h | Adds RESULT::want_network field for GUI RPC consumers. |
| lib/gui_rpc_client.cpp | Removes duplicate addr_len() helper (now in lib/network.h). |
| lib/gui_rpc_client_ops.cpp | Parses/clears want_network in GUI RPC result parsing. |
| clientgui/MainDocument.cpp | Shows “Waiting for network” in task status text. |
| client/result.h | Removes stored per-task final bytes sent/received fields. |
| client/result.cpp | Removes read/write of per-task final bytes sent/received in state output. |
| client/net_stats.h | Replaces show_ref_message with network_notice_active gating. |
| client/net_stats.cpp | Uses new some_task_wants_network() and adjusts notice clearing paths. |
| client/gui_http.h | Clarifies GUI_HTTP responsibilities in comments. |
| client/cs_notice.cpp | Removes additional network-related notices when clearing network messages. |
| client/client_state.h | Updates/expands network-related notice strings. |
| client/client_msgs.cpp | Adjusts msg_printf() to mark messages as non-HTML. |
| client/app.h | Removes legacy network byte accounting; redefines want_network semantics; renames task-set query. |
| client/app.cpp | Removes active-task network byte output; adds <want_network/> to GUI output; renames task-set query impl. |
| client/app_start.cpp | Removes resetting of removed per-episode byte counters. |
| client/app_control.cpp | Removes parsing of byte counters; adds notice logic for <want_network>. |
| api/boinc_api.h | Replaces legacy network APIs with boinc_waiting_for_network(bool). |
| api/boinc_api.cpp | Implements boinc_waiting_for_network() and includes <want_network> in app-status messages. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -1420,23 +1408,7 @@ bool ACTIVE_TASK::get_app_status_msg() { | |||
| parse_double(msg_buf, "<fpops_cumulative>", result->fpops_cumulative); | |||
| parse_double(msg_buf, "<intops_per_cpu_sec>", result->intops_per_cpu_sec); | |||
| parse_double(msg_buf, "<intops_cumulative>", result->intops_cumulative); | |||
| if (parse_double(msg_buf, "<bytes_sent>", dtemp)) { | |||
| if (dtemp > bytes_sent_episode) { | |||
| double nbytes = dtemp - bytes_sent_episode; | |||
| daily_xfer_history.add(nbytes, true); | |||
| bytes_sent += nbytes; | |||
| } | |||
| bytes_sent_episode = dtemp; | |||
| } | |||
| if (parse_double(msg_buf, "<bytes_received>", dtemp)) { | |||
| if (dtemp > bytes_received_episode) { | |||
| double nbytes = dtemp - bytes_received_episode; | |||
| daily_xfer_history.add(nbytes, false); | |||
| bytes_received += nbytes; | |||
| } | |||
| bytes_received_episode = dtemp; | |||
| } | |||
| parse_int(msg_buf, "<want_network>", want_network); | |||
| parse_int(msg_buf, "<want_network>", new_want_network); | |||
| if (parse_int(msg_buf, "<other_pid>", other_pid)) { | |||
| // for now, we handle only one of these | |||
| other_pids.clear(); | |||
| @@ -1445,6 +1417,32 @@ bool ACTIVE_TASK::get_app_status_msg() { | |||
| if (parse_int(msg_buf, "<sporadic_ac>", i)) { | |||
| sporadic_ac_state = (SPORADIC_AC_STATE)i; | |||
| } | |||
|
|
|||
| switch (new_want_network) { | |||
| case 0: | |||
| if (want_network) { | |||
| // app was waiting for network, now isn't. | |||
| if (net_status.network_notice_active) { | |||
| notices.remove_notices(NULL, REMOVE_NETWORK_MSG); | |||
| net_status.network_notice_active = false; | |||
| } | |||
| want_network = 0; | |||
| } | |||
| break; | |||
| case 1: | |||
| if (!want_network) { | |||
| if (!net_status.network_notice_active) { | |||
| if (gstate.network_suspended) { | |||
| msg_printf(0, MSG_USER_ALERT, APP_NETWORK_SUSPENDED_MSG); | |||
| } else { | |||
| msg_printf(0, MSG_USER_ALERT, APP_NEED_NETWORK_MSG); | |||
| } | |||
| net_status.network_notice_active = true; | |||
| } | |||
| want_network = 1; | |||
| } | |||
| break; | |||
| } | |||
| fprintf(stderr, "%s failed: %d\n", cmd, retval); | ||
| } | ||
| // ping exits nonzero on failure | ||
| return 0; |
| vector<string> out; | ||
| snprintf(cmd, sizeof(cmd), "build \"%s\" -t %s -f %s %s", | ||
| int retval; | ||
| snprintf(cmd, sizeof(cmd), "build \"%s\" --retry 0 -t %s -f %s %s", |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
client/app_control.cpp:1370
new_want_networkis initialized to -1 andwant_networkis no longer reset to 0 before parsing. Because the API only emits<want_network>when true, the tag will be absent when the app clears the condition, leavingnew_want_network == -1and preventingwant_networkfrom ever being cleared. Treat a missing<want_network>tag as 0 (e.g., initialize to 0 / restore reset-to-0 behavior) or have the API always send an explicit 0/1 value.
int new_want_network = -1;
current_cpu_time = checkpoint_cpu_time = 0.0;
if (parse_double(msg_buf, "<fraction_done>", fd)) {
// fraction_done will be reported as zero
// until the app's first call to boinc_fraction_done().
| fprintf(stderr, "%s failed: %d\n", cmd, retval); | ||
| } | ||
| // ping exits nonzero on failure | ||
| return 0; |
| // | ||
| int network_connected() { | ||
| #ifdef _WIN32 | ||
| const char* cmd = "ping google.com -n 1"; |
There was a problem hiding this comment.
@davidpanderson, can we have reference site configurable via cc_config.xml?
I personally don't care but some might not want to send any statistics to any corporate website (even via ping request).
It's ok to fallback to google.com if there is no site configured, but it would be nice to give users a choice.
See this ticket for reference.
There was a problem hiding this comment.
I added this (using the test_network_url that we already have in nvc_config.xml)
| fprintf(stderr, "%s failed: %d\n", cmd, retval); | ||
| } | ||
| // ping exits nonzero on failure | ||
| return 0; |
There was a problem hiding this comment.
@davidpanderson, please validate this logic. I believe you should return retval here
There was a problem hiding this comment.
Yup. Cubic caught that too.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
lib/util.cpp:318
- Changing
run_command()to takeconst char*is unsafe on Windows:CreateProcess()requires a writable command-line buffer (it may modify it), but the implementation casts away const ((LPTSTR)cmd). Callers can now legally pass string literals (e.g.network_connected()does), which can lead to undefined behavior/crashes. Either keep the parameter aschar*(enforcing mutability) or make an internal writable copy before callingCreateProcess().
int run_command(const char *cmd, vector<string> &out) {
out.clear();
#ifdef _WIN32
HANDLE pipe_read, pipe_write;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
memset(&sa, 0, sizeof(sa));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) return -1;
SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = pipe_write;
si.hStdError = pipe_write;
si.hStdInput = NULL;
if (!CreateProcess(
NULL,
(LPTSTR)cmd,
NULL,
lib/util.cpp:376
network_connected()relies onrun_command()returning nonzero whenpingfails, but on Unixrun_command()ignores the command’s exit status (it callspclose(fp)but doesn’t check its return value). As a result,network_connected()will almost always report “connected” on Unix even whenpingfails. Consider checking thepclose()status (e.g.WEXITSTATUS) and returning an error when the command exits nonzero.
int run_command(const char *cmd, vector<string> &out) {
out.clear();
#ifdef _WIN32
HANDLE pipe_read, pipe_write;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
memset(&sa, 0, sizeof(sa));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) return -1;
SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = pipe_write;
si.hStdError = pipe_write;
si.hStdInput = NULL;
if (!CreateProcess(
NULL,
(LPTSTR)cmd,
NULL,
NULL,
TRUE, // inherit handles
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi
)) {
return -1;
}
// wait for command to finish
//
WaitForSingleObject(pi.hProcess, INFINITE);
unsigned long exit_code;
GetExitCodeProcess(pi.hProcess, &exit_code);
if (exit_code) return -1;
DWORD count, nread;
PeekNamedPipe(pipe_read, NULL, NULL, NULL, &count, NULL);
if (count == 0) {
return 0;
}
char* buf = (char*)malloc(count+1);
if (!ReadFile(pipe_read, buf, count, &nread, NULL)) {
free(buf);
return -1;
}
buf[nread] = 0;
char* p = buf;
while (*p) {
char* q = strchr(p, '\n');
if (!q) break;
out.push_back(string(p, q-p+1)); // include \n
p = q + 1;
}
free(buf);
#else
#ifndef _USING_FCGI_
char buf[256];
errno = 0;
FILE* fp = popen(cmd, "r");
if (!fp) {
fprintf(stderr, "popen() failed: %s\n", cmd);
return ERR_FOPEN;
}
while (fgets(buf, 256, fp)) {
out.push_back(buf);
}
pclose(fp);
if (errno) {
fprintf(stderr, "popen() failed errno %d: %s\n", errno, cmd);
return -1;
}
#endif
#endif
return 0;
| // if google was previously unreachable, see if that's changed | ||
| if (google_unreachable) { | ||
| retval = network_connected(); | ||
| if (retval) { | ||
| if (verbose_all()) { | ||
| fprintf(stderr, | ||
| "google still unreachable (%d); sleeping 10\n", retval | ||
| ); | ||
| } | ||
| boinc_sleep(10); | ||
| continue; | ||
| } | ||
| } | ||
| retval = docker_conn.command(cmd, out, verbose_std()); | ||
| if (retval) { | ||
| fprintf(stderr, "build command failed: %d\n", retval); | ||
| return retval; | ||
| } | ||
| if (output_has_str(out, "retrying")) { | ||
| if (verbose_std()) { | ||
| fprintf(stderr, "build cmd output has 'retrying'\n"); | ||
| } | ||
| retval = network_connected(); | ||
| if (retval == 0) { | ||
| // network connection exists but the create operation | ||
| // couldn't reach a needed server; error out | ||
| if (verbose_std()) { | ||
| fprintf(stderr, "... but google is reachable; quitting\n"); | ||
| } | ||
| return -1; | ||
| } | ||
| if (verbose_std()) { | ||
| fprintf(stderr, "google is unreachable (%d); sleeping\n", retval); | ||
| } |
| @@ -1420,23 +1408,7 @@ bool ACTIVE_TASK::get_app_status_msg() { | |||
| parse_double(msg_buf, "<fpops_cumulative>", result->fpops_cumulative); | |||
| parse_double(msg_buf, "<intops_per_cpu_sec>", result->intops_per_cpu_sec); | |||
| parse_double(msg_buf, "<intops_cumulative>", result->intops_cumulative); | |||
| if (parse_double(msg_buf, "<bytes_sent>", dtemp)) { | |||
| if (dtemp > bytes_sent_episode) { | |||
| double nbytes = dtemp - bytes_sent_episode; | |||
| daily_xfer_history.add(nbytes, true); | |||
| bytes_sent += nbytes; | |||
| } | |||
| bytes_sent_episode = dtemp; | |||
| } | |||
| if (parse_double(msg_buf, "<bytes_received>", dtemp)) { | |||
| if (dtemp > bytes_received_episode) { | |||
| double nbytes = dtemp - bytes_received_episode; | |||
| daily_xfer_history.add(nbytes, false); | |||
| bytes_received += nbytes; | |||
| } | |||
| bytes_received_episode = dtemp; | |||
| } | |||
| parse_int(msg_buf, "<want_network>", want_network); | |||
| parse_int(msg_buf, "<want_network>", new_want_network); | |||
| if (parse_int(msg_buf, "<other_pid>", other_pid)) { | |||
| // for now, we handle only one of these | |||
| other_pids.clear(); | |||
| @@ -1445,6 +1417,38 @@ bool ACTIVE_TASK::get_app_status_msg() { | |||
| if (parse_int(msg_buf, "<sporadic_ac>", i)) { | |||
| sporadic_ac_state = (SPORADIC_AC_STATE)i; | |||
| } | |||
|
|
|||
| switch (new_want_network) { | |||
| case 0: | |||
| // if want_network goes true to false, | |||
| // and no tasks now want network, remove notice | |||
| // | |||
| if (want_network) { | |||
| want_network = 0; | |||
| if (net_status.network_notice_active) { | |||
| if (!gstate.active_tasks.some_task_wants_network()) { | |||
| notices.remove_notices(NULL, REMOVE_NETWORK_MSG); | |||
| net_status.network_notice_active = false; | |||
| } | |||
| } | |||
| } | |||
| break; | |||
| case 1: | |||
| // if want_network goes from false to true, show notice | |||
| // | |||
| if (!want_network) { | |||
| if (!net_status.network_notice_active) { | |||
| if (gstate.network_suspended) { | |||
| msg_printf(0, MSG_USER_ALERT, APP_NETWORK_SUSPENDED_MSG); | |||
| } else { | |||
| msg_printf(0, MSG_USER_ALERT, APP_NEED_NETWORK_MSG); | |||
| } | |||
| net_status.network_notice_active = true; | |||
| } | |||
| want_network = 1; | |||
| } | |||
| break; | |||
| } | |||
- nvc_config.xml has an optional 'network test URL'.
If this is present, pass it to apps via the app_init.xml file
- network_connected() now takes a hostname arg
- docker_wrapper: look for network test URL in APP_INIT_DATA.
If found convert it to a hostname;
else use "www.google.com".
Pass this to network_connected();
There was a problem hiding this comment.
3 issues found across 8 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| // (used w/ google.com to check for network connection) | ||
| // | ||
| // We used to do this directly, by gethostbyname() and connect(). | ||
| // But on Windows, gethostbyname() caches negative results, | ||
| // so once 'google.com' fails (due to network disconnection) |
| char cmd2[1024]; | ||
|
|
||
| // CreateProcess() can modify its cmd arg (WTF???) | ||
| // So copy it to a temp buffer | ||
| safe_strcpy(cmd2, cmd); |
| retval = docker_conn.command(cmd, out, verbose_std()); | ||
| if (retval) { | ||
| fprintf(stderr, "build command failed: %d\n", retval); | ||
| return retval; | ||
| } |
| // and no tasks now want network, remove notice | ||
| // | ||
| if (want_network) { | ||
| want_network = 0; | ||
| if (net_status.network_notice_active) { | ||
| if (!gstate.active_tasks.some_task_wants_network()) { | ||
| notices.remove_notices(NULL, REMOVE_NETWORK_MSG); | ||
| net_status.network_notice_active = false; | ||
| } | ||
| } |
| int LOOKUP_WEBSITE_OP::do_rpc(string& url) { | ||
| int retval; | ||
|
|
||
| if (net_status.show_ref_message) { | ||
| msg_printf(0, MSG_INFO, | ||
| "Project communication failed: attempting access to reference site" | ||
| ); | ||
| } | ||
| msg_printf(0, MSG_INFO, | ||
| "Project communication failed: attempting access to reference site" | ||
| ); | ||
| retval = gui_http->do_rpc(this, url.c_str(), LOOKUP_WEBSITE_FILENAME, true); |
fix ping command args
| bool network_connected() { | ||
| char cmd[256]; | ||
| snprintf(cmd, sizeof(cmd), "ping %s berkeley.edu", | ||
| #ifdef _WIN32 | ||
| "-n 1" | ||
| #else | ||
| "-c 1" | ||
| #endif | ||
| ); | ||
| vector<string> out; | ||
| int retval = run_command(cmd, out); | ||
| // ping exits nonzero on failure | ||
| if (retval) { | ||
| return false; | ||
| } |
| char cmd2[1024]; | ||
|
|
||
| // CreateProcess() can modify its cmd arg (WTF???) | ||
| // So copy it to a temp buffer | ||
| safe_strcpy(cmd2, cmd); |
| int build_image() { | ||
| char cmd[256]; | ||
| vector<string> out; | ||
| snprintf(cmd, sizeof(cmd), "build \"%s\" -t %s -f %s %s", | ||
| escaped_cwd, image_name, dockerfile, config.build_args.c_str() | ||
| int retval; | ||
|
|
||
| snprintf(cmd, sizeof(cmd), | ||
| "build \"%s\" %s -t %s -f %s %s", | ||
| escaped_cwd, | ||
| docker_type == PODMAN?"--retry 0":"", | ||
| image_name, dockerfile, config.build_args.c_str() | ||
| ); |
| if (p2->sched_req_no_work[i]) { | ||
| if (log_flags.work_fetch_debug) { | ||
| msg_printf(p, MSG_INFO, | ||
| "piggyback: %s doesn't have jobs", p2->project_name | ||
| ); | ||
| } | ||
| continue; | ||
| } |
| switch (new_want_network) { | ||
| case 0: | ||
| // if want_network goes true to false, | ||
| // and no tasks now want network, remove notice | ||
| // | ||
| if (want_network) { | ||
| want_network = 0; | ||
| if (net_status.network_notice_active) { | ||
| if (!gstate.active_tasks.some_task_wants_network()) { | ||
| notices.remove_notices(NULL, REMOVE_NETWORK_MSG); | ||
| net_status.network_notice_active = false; | ||
| } | ||
| } | ||
| } | ||
| break; | ||
| case 1: | ||
| // if want_network goes from false to true, show notice | ||
| // | ||
| if (!want_network) { | ||
| if (!net_status.network_notice_active) { | ||
| if (gstate.network_suspended) { | ||
| msg_printf(0, MSG_USER_ALERT, APP_NETWORK_SUSPENDED_MSG); | ||
| } else { | ||
| msg_printf(0, MSG_USER_ALERT, APP_NEED_NETWORK_MSG); | ||
| } | ||
| net_status.network_notice_active = true; | ||
| } | ||
| want_network = 1; | ||
| } | ||
| break; | ||
| } |
| bool network_connected() { | ||
| char cmd[256]; | ||
| snprintf(cmd, sizeof(cmd), "ping %s berkeley.edu", | ||
| #ifdef _WIN32 | ||
| "-n 1" | ||
| #else | ||
| "-c 1" | ||
| #endif | ||
| ); | ||
| vector<string> out; | ||
| int retval = run_command(cmd, out); | ||
| // ping exits nonzero on failure | ||
| if (retval) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } |
| if (!want_network) { | ||
| if (!net_status.network_notice_active) { | ||
| if (gstate.network_suspended) { | ||
| msg_printf(0, MSG_USER_ALERT, APP_NETWORK_SUSPENDED_MSG); | ||
| } else { | ||
| msg_printf(0, MSG_USER_ALERT, APP_NEED_NETWORK_MSG); | ||
| } | ||
| net_status.network_notice_active = true; |
| @@ -253,11 +251,9 @@ void LOOKUP_WEBSITE_OP::handle_reply(int http_op_retval) { | |||
| net_status.last_comm_time = 0; | |||
| show_fail_msg(); | |||
| } else { | |||
| if (net_status.show_ref_message) { | |||
| msg_printf(0, MSG_INFO, | |||
| "Internet access OK - project servers may be temporarily down." | |||
| ); | |||
| } | |||
| msg_printf(0, MSG_INFO, | |||
| "Internet access OK - project servers may be temporarily down." | |||
| ); | |||
| } | |||
| if (p2->sched_req_no_work[i]) { | ||
| if (log_flags.work_fetch_debug) { | ||
| msg_printf(p, MSG_INFO, | ||
| "piggyback: %s doesn't have jobs", p2->project_name | ||
| ); | ||
| } | ||
| continue; | ||
| } |
| #define NEED_NETWORK_MSG _("BOINC can't access Internet - check network connection") | ||
| #define APP_NEED_NETWORK_MSG _("Tasks can't access Internet - check network connection") | ||
| #define APP_NETWORK_SUSPENDED_MSG _("Tasks need Internet access - consider unsuspending network") |
| static int want_network = 0; | ||
| static int have_network = 1; | ||
| static double bytes_sent = 0; | ||
| static double bytes_received = 0; |
| char cmd2[1024]; | ||
|
|
||
| // CreateProcess() can modify its cmd arg (WTF???) | ||
| // So copy it to a temp buffer | ||
| safe_strcpy(cmd2, cmd); | ||
|
|
| if (!CreateProcess( | ||
| NULL, | ||
| (LPTSTR)cmd, | ||
| (LPTSTR)cmd2, | ||
| NULL, | ||
| NULL, | ||
| TRUE, // inherit handles |
client: keep track of suspend/connection notices separately run_command() Win: return output even if command fails
The goal of this PR is to allow apps that do network communication
to work properly in the presence of network outages and suspension.
With these changes:
If a job that needs network (such as a docker job) runs while
the network is disconnected, it figures this out, tells the client,
and the user is shown a notice suggesting that they reconnect.
If such a job needs network and network access is suspended by the user
(via time-of-day prefs or the 'suspend network' command)
it sees this, tells the client that it needs to communicate,
and the user is shown a notice suggesting that they unsuspend.
This requires some logic in apps; this PR adds this to docker_wrapper.
This is described here:
https://github.com/BOINC/boinc/wiki/Apps-that-do-network-communication
Note: in ~2004 there was an unsuccessful effort to run Folding@home
as a BOINC app, and we added logic where the client is responsible
for knowing whether a connection exists, and it notifies the app.
This PR removes this logic; in the new design it's up to the app
to detect the absence of a connection.
Remove an API where the app tells the client how much data it transferred.
This info isn't used, and in the case of Docker the app doesn't know
how much data was transferred.
Summary by cubic
Apps can now tell the client when they’re waiting for Internet, and the UI shows clear notices and a “Waiting for network” task state.
docker_wrapperdetects outages, retries image builds until connectivity returns, and surfaces better errors.New Features
boinc_waiting_for_network(bool); app status always includes<want_network>; removedboinc_need_network(),boinc_network_poll(),boinc_network_done(),boinc_network_usage().want_network; don’t strip tags in notices; removed unused bytes sent/received tracking.docker_wrapper: waits for a heartbeat before checking suspend state; detects disconnect/suspension, setswant_network, and retries image builds until network returns; checks connectivity via ping toberkeley.edu; honors exit/abort while waiting; prints network-related logs only with verbose flags; shows command output on errors; uses--retry 0for Podman builds; on Windows, copies the command string beforeCreateProcess.run_command()returns non‑zero on command failures and (on Windows) still returns command output; addednetwork_connected().Migration
boinc_waiting_for_network(bool).boinc_waiting_for_network(true/false); include<want_network>in status updates.Written for commit cbe058e. Summary will update on new commits. Review in cubic